﻿using System;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using UtilZ.Dotnet.Ex.Model;
using UtilZ.Dotnet.Ex.ZLog;

namespace UtilZ.Dotnet.Ex.Base
{
#pragma warning disable 8500

    /// <summary>
    /// 文件类扩展方法类[分区文本文件创]
    /// </summary>
    unsafe public static partial class FileEx
    {
        /// <summary>
        /// 创建需要分隔大小的文件路径[2014-10-27_1.log]
        /// </summary>
        /// <param name="createTime">创建文件日期</param>
        /// <param name="lastTime">上次记录日志文件的日期</param>
        /// <param name="index">当前文件索引,没有记录之前的初始值为-1</param>
        /// <param name="dateFormat">日期格式</param>
        /// <param name="directory">文件存放目录</param>
        /// <param name="fileName">文件名</param>
        /// <param name="extension">文件扩展名</param>
        /// <param name="fileSize">文件分隔大小,单位/MB</param>
        /// <returns>文件路径</returns>
        public static string CreateFilePath(DateTime createTime, ref DateTime lastTime, ref int index, string dateFormat, string directory, string fileName, string extension, uint fileSize)
        {
            //参数检查
            FileEx.CreateFilePathParaCheck(index, dateFormat, directory, fileSize);

            if (index == -1)
            {
                index = FileEx.UpdateFileIndex(directory, extension, createTime, dateFormat, fileName);
            }
            else
            {
                if (lastTime.Year != createTime.Year || lastTime.DayOfYear != createTime.DayOfYear)
                {
                    //如果日期发生了变化,则重置为1
                    index = 1;
                    lastTime = createTime;
                }
            }

            string dayStr = createTime.ToString(dateFormat);
            string filePath = System.IO.Path.Combine(directory, string.Format(@"{0}{1}_{2}{3}", dayStr, fileName, index, extension));

            if (File.Exists(filePath))
            {
                while (true)
                {
                    try
                    {
                        FileInfo fileInfo = new FileInfo(filePath);
                        if (fileInfo.Length / 1024 / 1024 >= fileSize)//如果文件大小大于了fileSize大小，就将文件区分索引+1，再重新创建一个文件路径
                        {
                            index++;
                            filePath = Path.Combine(directory, string.Format(@"{0}_{1}{2}", dayStr, index, extension));
                            if (!File.Exists(filePath))
                            {
                                break;
                            }
                        }
                        else
                        {
                            break;
                        }
                    }
                    catch
                    {
                        index++;
                        filePath = Path.Combine(directory, string.Format(@"{0}_{1}{2}", dayStr, index, extension));
                        if (!File.Exists(filePath))
                        {
                            break;
                        }
                    }
                }
            }

            return filePath;
        }

        /// <summary>
        /// 创建需要分隔大小的文件路径[2014-10-27_1.log]方法参数验证
        /// </summary>
        /// <param name="index">当前文件索引,没有记录之前的初始值为-1</param>
        /// <param name="dateFormat">日期格式</param>
        /// <param name="directory">文件存放目录</param>
        /// <param name="fileSize">文件分隔大小</param>
        /// <returns>文件路径</returns>
        private static void CreateFilePathParaCheck(int index, string dateFormat, string directory, uint fileSize)
        {
            if (index == 0)
            {
                throw new ArgumentException(string.Format("文件索引:{0}不是有效值", index));
            }

            try
            {
                if (string.IsNullOrEmpty(dateFormat))
                {
                    throw new ArgumentException("日期转换格式参数不能为空");
                }
                DateTime.Now.ToString(dateFormat);
            }
            catch
            {
                throw new ArgumentException(string.Format("日期转换格式参数不是有效的格式化参数:{0}", dateFormat));
            }

            try
            {
                if (string.IsNullOrEmpty(directory))
                {
                    throw new ArgumentException("文件存放目录不能为空");
                }

                DirectoryInfo dirInfo = new DirectoryInfo(directory);
            }
            catch
            {
                throw new ArgumentException(string.Format("文件存放目录不是有效的参数:{0}", directory));
            }

            if (fileSize <= 0)
            {
                throw new ArgumentException("文件分隔大小不能小于0");
            }
        }

        /// <summary>
        /// 更新文件索引
        /// </summary>
        /// <param name="directory">文件存放目录</param>
        /// <param name="extension">文件扩展名</param>
        /// <param name="dt">日期时间</param>
        /// <param name="dateFormat">日期格式</param>
        /// <param name="fileName">文件名</param>
        /// <returns>文件索引</returns>
        private static int UpdateFileIndex(string directory, string extension, DateTime dt, string dateFormat, string fileName)
        {
            int logIndex = 1;
            int tmpLogIndex = -1;
            string regLogFileName = string.Format(@"^{0}{1}_(?<index>\d+){2}$", dt.ToString(dateFormat), fileName, extension);
            Regex logPathRex = new Regex(regLogFileName);
            try
            {
                DirectoryInfo dirInfo = new DirectoryInfo(directory);
                if (!dirInfo.Exists)
                {
                    return logIndex;
                }

                string[] filePaths = Directory.GetFiles(directory, "*" + extension);
                Match logPathMatch = null;

                foreach (string filePath in filePaths)
                {
                    logPathMatch = logPathRex.Match(Path.GetFileName(filePath));
                    if (!logPathMatch.Success)
                    {
                        continue;
                    }

                    tmpLogIndex = int.Parse(logPathMatch.Groups["index"].Value);
                    if (tmpLogIndex > logIndex)
                    {
                        logIndex = tmpLogIndex;
                    }
                }
            }
            catch
            { }
            return logIndex;
        }

        /// <summary>
        /// 获取分区文件的创建日期字符串
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <returns>分区文件的创建日期字符串</returns>
        public static string GetFileCreateDate(string filePath)
        {
            string fileName = Path.GetFileNameWithoutExtension(filePath);
            int index = fileName.IndexOf("_");
            if (index != -1)
            {
                fileName = fileName.Substring(0, fileName.IndexOf("_"));
            }
            return fileName;
        }

        /// <summary>
        /// 尝试删除文件[删除成功返回true;失败返回false]
        /// </summary>
        /// <param name="filePath">目标文件路由</param>
        /// <returns>删除成功返回true;失败返回false</returns>
        public static bool TryDeleFile(string filePath)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(filePath))
                {
                    return false;
                }

                if (!File.Exists(filePath))
                {
                    return true;
                }

                File.Delete(filePath);
                return true;
            }
            catch (Exception ex)
            {
                ZLoger.Warn(ex);
                return false;
            }
        }

        /// <summary>
        /// 获取文件大小
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <returns>文件大小</returns>
        public static long GetFileLength(string filePath)
        {
            if (string.IsNullOrWhiteSpace(filePath))
            {
                throw new ArgumentNullException(nameof(filePath));
            }

            if (!File.Exists(filePath))
            {
                throw new FileNotFoundException("文件不存在", filePath);
            }

            long length;
            using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
            {
                length = fs.Length;
            }
            return length;
        }



        #region 将数据写入到文件

        private static FileMode CheckAppendFileMode(string filePath)
        {
            FileMode fileMode;
            if (File.Exists(filePath))
            {
                fileMode = FileMode.Append;
            }
            else
            {
                fileMode = FileMode.OpenOrCreate;
            }

            return fileMode;
        }

        private static void PrimitiveWriteIQSpanZ<T>(IQSpanZ<T> span, string filePath, FileMode fileMode) where T : struct
        {
            if (span.Length <= 0)
            {
                return;
            }

            DirectoryInfoEx.CheckFilePathDirectory(filePath);
            using (var fileStream = new FileStream(filePath, fileMode, FileAccess.Write))
            {
                var writer = new BinaryWriter(fileStream);
                PrimitiveDataTypes dataType = TypeEx.GetPrimitiveDataType(span.IDataPtr[0]);
                switch (dataType)
                {
                    case PrimitiveDataTypes.SByte:
                        sbyte* sbyteIDaataPtr = (sbyte*)span.IDataPtr;
                        sbyte* sbyteQDaataPtr = (sbyte*)span.QDataPtr;
                        for (int i = 0; i < span.Length; i++)
                        {
                            writer.Write(*sbyteIDaataPtr++);
                            writer.Write(*sbyteQDaataPtr++);
                        }
                        break;
                    case PrimitiveDataTypes.Byte:
                        byte* byteIDaataPtr = (byte*)span.IDataPtr;
                        byte* byteQDaataPtr = (byte*)span.QDataPtr;
                        for (int i = 0; i < span.Length; i++)
                        {
                            writer.Write(*byteIDaataPtr++);
                            writer.Write(*byteQDaataPtr++);
                        }
                        break;
                    case PrimitiveDataTypes.Int16:
                        Int16* int16IDaataPtr = (Int16*)span.IDataPtr;
                        Int16* int16QDaataPtr = (Int16*)span.QDataPtr;
                        for (int i = 0; i < span.Length; i++)
                        {
                            writer.Write(*int16IDaataPtr++);
                            writer.Write(*int16QDaataPtr++);
                        }
                        break;
                    case PrimitiveDataTypes.UInt16:
                        UInt16* uint16IDaataPtr = (UInt16*)span.IDataPtr;
                        UInt16* uint16QDaataPtr = (UInt16*)span.QDataPtr;
                        for (int i = 0; i < span.Length; i++)
                        {
                            writer.Write(*uint16IDaataPtr++);
                            writer.Write(*uint16QDaataPtr++);
                        }
                        break;
                    case PrimitiveDataTypes.Int32:
                        Int32* int32IDaataPtr = (Int32*)span.IDataPtr;
                        Int32* int32QDaataPtr = (Int32*)span.QDataPtr;
                        for (int i = 0; i < span.Length; i++)
                        {
                            writer.Write(*int32IDaataPtr++);
                            writer.Write(*int32QDaataPtr++);
                        }
                        break;
                    case PrimitiveDataTypes.UInt32:
                        UInt32* uint32IDaataPtr = (UInt32*)span.IDataPtr;
                        UInt32* uint32QDaataPtr = (UInt32*)span.QDataPtr;
                        for (int i = 0; i < span.Length; i++)
                        {
                            writer.Write(*uint32IDaataPtr++);
                            writer.Write(*uint32QDaataPtr++);
                        }
                        break;
                    case PrimitiveDataTypes.Int64:
                        Int64* int64IDaataPtr = (Int64*)span.IDataPtr;
                        Int64* int64QDaataPtr = (Int64*)span.QDataPtr;
                        for (int i = 0; i < span.Length; i++)
                        {
                            writer.Write(*int64IDaataPtr++);
                            writer.Write(*int64QDaataPtr++);
                        }
                        break;
                    case PrimitiveDataTypes.UInt64:
                        UInt64* uint64IDaataPtr = (UInt64*)span.IDataPtr;
                        UInt64* uint64QDaataPtr = (UInt64*)span.QDataPtr;
                        for (int i = 0; i < span.Length; i++)
                        {
                            writer.Write(*uint64IDaataPtr++);
                            writer.Write(*uint64QDaataPtr++);
                        }
                        break;
                    case PrimitiveDataTypes.Float:
                        float* floatIDaataPtr = (float*)span.IDataPtr;
                        float* floatQDaataPtr = (float*)span.QDataPtr;
                        for (int i = 0; i < span.Length; i++)
                        {
                            writer.Write(*floatIDaataPtr++);
                            writer.Write(*floatQDaataPtr++);
                        }
                        break;
                    case PrimitiveDataTypes.Double:
                        double* doubleIDaataPtr = (double*)span.IDataPtr;
                        double* doubleQDaataPtr = (double*)span.QDataPtr;
                        for (int i = 0; i < span.Length; i++)
                        {
                            writer.Write(*doubleIDaataPtr++);
                            writer.Write(*doubleQDaataPtr++);
                        }
                        break;
                    case PrimitiveDataTypes.Decimal:
                        decimal* decimalIDaataPtr = (decimal*)span.IDataPtr;
                        decimal* decimalQDaataPtr = (decimal*)span.QDataPtr;
                        for (int i = 0; i < span.Length; i++)
                        {
                            writer.Write(*decimalIDaataPtr++);
                            writer.Write(*decimalQDaataPtr++);
                        }
                        break;
#if CORE
                    case PrimitiveDataTypes.Half:
                        Half* halfIDaataPtr = (Half*)span.IDataPtr;
                        Half* halfQDaataPtr = (Half*)span.QDataPtr;
                        for (int i = 0; i < span.Length; i++)
                        {
                            writer.Write(*halfIDaataPtr++);
                            writer.Write(*halfQDaataPtr++);
                        }
                        break;
#endif
                    default:
                        throw new NotImplementedException($"未实现的数据类型\"{dataType.ToString()}\"写文件");
                }
            }
        }

        private static void PrimitiveWriteSpan(Span<Complex> span, string filePath, FileMode fileMode)
        {
            if (span.Length <= 0)
            {
                return;
            }

            DirectoryInfoEx.CheckFilePathDirectory(filePath);
            using (var fileStream = new FileStream(filePath, fileMode, FileAccess.Write))
            {
                var writer = new BinaryWriter(fileStream);
                for (int i = 0; i < span.Length; i++)
                {
                    writer.Write(span[i].Real);
                    writer.Write(span[i].Imaginary);
                }
            }
        }

        private static void PrimitiveWriteSpanZ(SpanZ<Complex> span, string filePath, FileMode fileMode)
        {
            if (span.Length <= 0)
            {
                return;
            }

            DirectoryInfoEx.CheckFilePathDirectory(filePath);
            using (var fileStream = new FileStream(filePath, fileMode, FileAccess.Write))
            {
                var writer = new BinaryWriter(fileStream);
                for (int i = 0; i < span.Length; i++)
                {
                    writer.Write(span[i].Real);
                    writer.Write(span[i].Imaginary);
                }
            }
        }

        private static void PrimitiveWriteBuffer<T>(T[] buffer, int startIndex, int length, string filePath, FileMode fileMode) where T : struct
        {
            if (length <= 0)
            {
                return;
            }

            DirectoryInfoEx.CheckFilePathDirectory(filePath);
            using (var fileStream = new FileStream(filePath, fileMode, FileAccess.Write))
            {
                int end = startIndex + length;
                var writer = new BinaryWriter(fileStream);
                PrimitiveDataTypes dataType = TypeEx.GetPrimitiveDataType(buffer[0]);
                switch (dataType)
                {
                    case PrimitiveDataTypes.SByte:
                        var sbyteBuffer = (sbyte[])((object)buffer);
                        for (int i = startIndex; i < end; i++)
                        {
                            writer.Write(sbyteBuffer[i]);
                        }
                        break;
                    case PrimitiveDataTypes.Byte:
                        var byteBuffer = (byte[])((object)buffer);
                        fileStream.Write(byteBuffer, startIndex, length);
                        break;
                    case PrimitiveDataTypes.Int16:
                        var shortBuffer = (short[])((object)buffer);
                        for (int i = startIndex; i < end; i++)
                        {
                            writer.Write(shortBuffer[i]);
                        }
                        break;
                    case PrimitiveDataTypes.UInt16:
                        var ushortBuffer = (ushort[])((object)buffer);
                        for (int i = startIndex; i < end; i++)
                        {
                            writer.Write(ushortBuffer[i]);
                        }
                        break;
                    case PrimitiveDataTypes.Int32:
                        var intBuffer = (int[])((object)buffer);
                        for (int i = startIndex; i < end; i++)
                        {
                            writer.Write(intBuffer[i]);
                        }
                        break;
                    case PrimitiveDataTypes.UInt32:
                        var uintBuffer = (uint[])((object)buffer);
                        for (int i = startIndex; i < end; i++)
                        {
                            writer.Write(uintBuffer[i]);
                        }
                        break;
                    case PrimitiveDataTypes.Int64:
                        var longBuffer = (long[])((object)buffer);
                        for (int i = startIndex; i < end; i++)
                        {
                            writer.Write(longBuffer[i]);
                        }
                        break;
                    case PrimitiveDataTypes.UInt64:
                        var ulongBuffer = (ulong[])((object)buffer);
                        for (int i = startIndex; i < end; i++)
                        {
                            writer.Write(ulongBuffer[i]);
                        }
                        break;
                    case PrimitiveDataTypes.Float:
                        var floatBuffer = (float[])((object)buffer);
                        for (int i = startIndex; i < end; i++)
                        {
                            writer.Write(floatBuffer[i]);
                        }
                        break;
                    case PrimitiveDataTypes.Double:
                        var doubleBuffer = (double[])((object)buffer);
                        for (int i = startIndex; i < end; i++)
                        {
                            writer.Write(doubleBuffer[i]);
                        }
                        break;
                    case PrimitiveDataTypes.Decimal:
                        var decimalBuffer = (decimal[])((object)buffer);
                        for (int i = startIndex; i < end; i++)
                        {
                            writer.Write(decimalBuffer[i]);
                        }
                        break;
#if CORE
                    case PrimitiveDataTypes.Half:
                        var halfBuffer = (Half[])((object)buffer);
                        for (int i = startIndex; i < end; i++)
                        {
                            writer.Write(halfBuffer[i]);
                        }
                        break;
#endif
                    default:
                        throw new NotImplementedException($"未实现的数据类型\"{dataType.ToString()}\"写文件");
                }
            }
        }

        private static void PrimitiveWriteSpanBuffer<T>(Span<T> span, string filePath, FileMode fileMode) where T : struct
        {
            DirectoryInfoEx.CheckFilePathDirectory(filePath);
            using (var fileStream = new FileStream(filePath, fileMode, FileAccess.Write))
            {
                var writer = new BinaryWriter(fileStream);
                PrimitiveDataTypes dataType = TypeEx.GetPrimitiveDataType(span[0]);
#if NET4_0
                PrimitiveWritePtr(writer, dataType, span.DataPtr, span.Length);
#else
                void* ptr = System.Runtime.CompilerServices.Unsafe.AsPointer<T>(ref span[0]);
                PrimitiveWritePtr(writer, dataType, ptr, span.Length);
#endif
            }
        }

        private static void PrimitiveWriteSpanZBuffer<T>(SpanZ<T> span, string filePath, FileMode fileMode) where T : struct
        {
            if (span.Length <= 0)
            {
                return;
            }

            DirectoryInfoEx.CheckFilePathDirectory(filePath);
            using (var fileStream = new FileStream(filePath, fileMode, FileAccess.Write))
            {
#if CORE
                fileStream.Write(new Span<byte>(span.DataPtr, span.Length * sizeof(T)));
#else
                var writer = new BinaryWriter(fileStream);
                PrimitiveDataTypes dataType = TypeEx.GetPrimitiveDataType(span[0]);
                PrimitiveWritePtr(writer, dataType, span.DataPtr, span.Length);
#endif
            }
        }

        private static void PrimitiveWritePtr(BinaryWriter writer, PrimitiveDataTypes dataType, void* dataPtr, int length)
        {
            switch (dataType)
            {
                case PrimitiveDataTypes.SByte:
                    sbyte* sbyteDataPtr = (sbyte*)dataPtr;
                    for (int i = 0; i < length; i++)
                    {
                        writer.Write(*sbyteDataPtr++);
                    }
                    break;
                case PrimitiveDataTypes.Byte:
                    byte* byteDataPtr = (byte*)dataPtr;
                    for (int i = 0; i < length; i++)
                    {
                        writer.Write(*byteDataPtr++);
                    }
                    break;
                case PrimitiveDataTypes.Int16:
                    Int16* int16DataPtr = (Int16*)dataPtr;
                    for (int i = 0; i < length; i++)
                    {
                        writer.Write(*int16DataPtr++);
                    }
                    break;
                case PrimitiveDataTypes.UInt16:
                    UInt16* uint16DataPtr = (UInt16*)dataPtr;
                    for (int i = 0; i < length; i++)
                    {
                        writer.Write(*uint16DataPtr++);
                    }
                    break;
                case PrimitiveDataTypes.Int32:
                    Int32* int32DataPtr = (Int32*)dataPtr;
                    for (int i = 0; i < length; i++)
                    {
                        writer.Write(*int32DataPtr++);
                    }
                    break;
                case PrimitiveDataTypes.UInt32:
                    UInt32* uint32DataPtr = (UInt32*)dataPtr;
                    for (int i = 0; i < length; i++)
                    {
                        writer.Write(*uint32DataPtr++);
                    }
                    break;
                case PrimitiveDataTypes.Int64:
                    Int64* int64DataPtr = (Int64*)dataPtr;
                    for (int i = 0; i < length; i++)
                    {
                        writer.Write(*int64DataPtr++);
                    }
                    break;
                case PrimitiveDataTypes.UInt64:
                    UInt64* uint64DataPtr = (UInt64*)dataPtr;
                    for (int i = 0; i < length; i++)
                    {
                        writer.Write(*uint64DataPtr++);
                    }
                    break;
                case PrimitiveDataTypes.Float:
                    float* floatDataPtr = (float*)dataPtr;
                    for (int i = 0; i < length; i++)
                    {
                        writer.Write(*floatDataPtr++);
                    }
                    break;
                case PrimitiveDataTypes.Double:
                    double* doubleDataPtr = (double*)dataPtr;
                    for (int i = 0; i < length; i++)
                    {
                        writer.Write(*doubleDataPtr++);
                    }
                    break;
                case PrimitiveDataTypes.Decimal:
                    decimal* decimalDataPtr = (decimal*)dataPtr;
                    for (int i = 0; i < length; i++)
                    {
                        writer.Write(*decimalDataPtr++);
                    }
                    break;
#if CORE
                case PrimitiveDataTypes.Half:
                    Half* halfDataPtr = (Half*)dataPtr;
                    for (int i = 0; i < length; i++)
                    {
                        writer.Write(*halfDataPtr++);
                    }
                    break;
#endif
                default:
                    throw new NotImplementedException($"未实现的数据类型\"{dataType.ToString()}\"写文件");
            }
        }




        /// <summary>
        /// 写入数据到文件,文件已存在则删除旧的重新创建
        /// </summary>
        /// <param name="span">数据buffer</param>
        /// <param name="filePath">要写入的文件路径</param>
        public static void Write<T>(IQSpanZ<T> span, string filePath) where T : struct
        {
            PrimitiveWriteIQSpanZ(span, filePath, FileMode.CreateNew);
        }

        /// <summary>
        /// 写入数据到文件,文件已存在则删除旧的重新创建
        /// </summary>
        /// <param name="span">数据buffer</param>
        /// <param name="filePath">要写入的文件路径</param>
        public static void Write(Span<Complex> span, string filePath)
        {
            PrimitiveWriteSpan(span, filePath, FileMode.CreateNew);
        }


        /// <summary>
        /// 写入数据到文件,文件已存在则删除旧的重新创建
        /// </summary>
        /// <param name="span">数据buffer</param>
        /// <param name="filePath">要写入的文件路径</param>
        public static void Write(SpanZ<Complex> span, string filePath)
        {
            PrimitiveWriteSpanZ(span, filePath, FileMode.CreateNew);
        }

        /// <summary>
        /// 写入数据到文件,文件已存在则删除旧的重新创建
        /// </summary>
        /// <typeparam name="T">数据类型,只能为值类型</typeparam>
        /// <param name="buffer">数据buffer</param>
        /// <param name="filePath">要写入的文件路径</param>
        public static void Write<T>(T[] buffer, string filePath) where T : struct
        {
            if (buffer == null)
            {
                throw new ArgumentNullException(nameof(buffer));
            }

            PrimitiveWriteBuffer(buffer, 0, buffer.Length, filePath, FileMode.CreateNew);
        }

        /// <summary>
        /// 写入数据到文件,文件已存在则删除旧的重新创建
        /// </summary>
        /// <typeparam name="T">数据类型,只能为值类型</typeparam>
        /// <param name="buffer">数据buffer</param>
        /// <param name="startIndex">数据buffer中的起始位置</param>
        /// <param name="length">要追加到文件的数据</param>
        /// <param name="filePath">要写入的文件路径</param>
        public static void Write<T>(T[] buffer, int startIndex, int length, string filePath) where T : struct
        {
            PrimitiveWriteBuffer(buffer, startIndex, length, filePath, FileMode.CreateNew);
        }

        /// <summary>
        /// 写入数据到文件,文件已存在则删除旧的重新创建
        /// </summary>
        /// <param name="span">数据buffer</param>
        /// <param name="filePath">要写入的文件路径</param>
        public static void Write<T>(Span<T> span, string filePath) where T : struct
        {
            PrimitiveWriteSpanBuffer(span, filePath, FileMode.CreateNew);
        }

        /// <summary>
        /// 写入数据到文件,文件已存在则删除旧的重新创建
        /// </summary>
        /// <param name="span">数据buffer</param>
        /// <param name="filePath">要写入的文件路径</param>
        public static void Write<T>(SpanZ<T> span, string filePath) where T : struct
        {
            PrimitiveWriteSpanZBuffer(span, filePath, FileMode.CreateNew);
        }




        /// <summary>
        /// 追加数据到文件,文件不存在则创建
        /// </summary>
        /// <param name="span">数据buffer</param>
        /// <param name="filePath">要追加的文件路径</param>
        public static void Append<T>(IQSpanZ<T> span, string filePath) where T : struct
        {
            PrimitiveWriteIQSpanZ(span, filePath, CheckAppendFileMode(filePath));
        }

        /// <summary>
        /// 追加数据到文件,文件不存在则创建
        /// </summary>
        /// <param name="span">数据buffer</param>
        /// <param name="filePath">要追加的文件路径</param>
        public static void Append(Span<Complex> span, string filePath)
        {
            PrimitiveWriteSpan(span, filePath, CheckAppendFileMode(filePath));
        }

        /// <summary>
        /// 追加数据到文件,文件不存在则创建
        /// </summary>
        /// <param name="span">数据buffer</param>
        /// <param name="filePath">要追加的文件路径</param>
        public static void Append(SpanZ<Complex> span, string filePath)
        {
            PrimitiveWriteSpanZ(span, filePath, CheckAppendFileMode(filePath));
        }

        /// <summary>
        /// 追加数据到文件,文件不存在则创建
        /// </summary>
        /// <typeparam name="T">数据类型,只能为值类型</typeparam>
        /// <param name="buffer">数据buffer</param>
        /// <param name="filePath">要追加的文件路径</param>
        public static void Append<T>(T[] buffer, string filePath) where T : struct
        {
            if (buffer == null)
            {
                throw new ArgumentNullException(nameof(buffer));
            }

            PrimitiveWriteBuffer(buffer, 0, buffer.Length, filePath, CheckAppendFileMode(filePath));
        }

        /// <summary>
        /// 追加数据到文件,文件不存在则创建
        /// </summary>
        /// <typeparam name="T">数据类型,只能为值类型</typeparam>
        /// <param name="buffer">数据buffer</param>
        /// <param name="startIndex">数据buffer中的起始位置</param>
        /// <param name="length">要追加到文件的数据</param>
        /// <param name="filePath">要追加的文件路径</param>
        public static void Append<T>(T[] buffer, int startIndex, int length, string filePath) where T : struct
        {
            PrimitiveWriteBuffer(buffer, startIndex, length, filePath, CheckAppendFileMode(filePath));
        }

        /// <summary>
        /// 追加数据到文件,文件不存在则创建
        /// </summary>
        /// <param name="span">数据buffer</param>
        /// <param name="filePath">要追加的文件路径</param>
        public static void Append<T>(Span<T> span, string filePath) where T : struct
        {
            PrimitiveWriteSpanBuffer(span, filePath, CheckAppendFileMode(filePath));
        }

        /// <summary>
        /// 追加数据到文件,文件不存在则创建
        /// </summary>
        /// <param name="span">数据buffer</param>
        /// <param name="filePath">要追加的文件路径</param>
        public static void Append<T>(SpanZ<T> span, string filePath) where T : struct
        {
            PrimitiveWriteSpanZBuffer(span, filePath, CheckAppendFileMode(filePath));
        }

        #endregion


        #region 从文件读取数据

        /// <summary>
        /// 从文件读取IQ数据
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="position">读取数据起始位置</param>
        /// <param name="fileTypeCode">文件中存储的数据类型</param>
        /// <param name="length">要读取的数据点数</param>
        /// <returns>读取到的数据</returns>
        public static Complex[] ReadIQ(string filePath, long position, TypeCode fileTypeCode, int length)
        {
            var buffer = new Complex[length];
            int readLen;
#if NET4_0
            fixed (Complex* dataPtr = buffer)
            {
                readLen = ReadIQ(filePath, position, fileTypeCode, new Span<Complex>(dataPtr, length));
            }
#else
            readLen = ReadIQ(filePath, position, fileTypeCode, new Span<Complex>(buffer));
#endif

            if (readLen != length)
            {
                if (readLen == 0)
                {
                    return new Complex[0];
                }
                else
                {
                    return buffer.Take(readLen).ToArray();
                }
            }
            else
            {
                return buffer;
            }
        }

        /// <summary>
        /// 从文件读取IQ数据
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="position">读取数据起始位置</param>
        /// <param name="fileTypeCode">文件中存储的数据类型</param>
        /// <param name="span">数据缓存</param>
        /// <returns>读取到的数据长度</returns>
        public static int ReadIQ(string filePath, long position, TypeCode fileTypeCode, SpanZ<Complex> span)
        {
            return ReadIQ(filePath, position, fileTypeCode, span.ToSpan());
        }

        /// <summary>
        /// 从文件读取IQ数据
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="position">读取数据起始位置</param>
        /// <param name="fileTypeCode">文件中存储的数据类型</param>
        /// <param name="span">数据缓存</param>
        /// <returns>读取到的数据长度</returns>
        /// <exception cref="NotImplementedException"></exception>
        public static int ReadIQ(string filePath, long position, TypeCode fileTypeCode, Span<Complex> span)
        {
            if (!File.Exists(filePath))
            {
                return 0;
            }

            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                if (filePath.Length <= position)
                {
                    return 0;
                }

                fileStream.Position = position;
                var canReadLength = fileStream.Length - position;

                var reader = new BinaryReader(fileStream);
                long length;
                double iData, qData;
                switch (fileTypeCode)
                {
                    case TypeCode.SByte:
                        length = canReadLength / (2 * sizeof(sbyte));
                        if (length > span.Length)
                        {
                            length = span.Length;
                        }

                        for (int i = 0; i < length; i++)
                        {
                            iData = reader.ReadSByte();
                            qData = reader.ReadSByte();
                            span[i] = new Complex(iData, qData);
                        }
                        break;
                    case TypeCode.Int16:
                        length = canReadLength / (2 * sizeof(short));
                        if (length > span.Length)
                        {
                            length = span.Length;
                        }

                        for (int i = 0; i < length; i++)
                        {
                            iData = reader.ReadInt16();
                            qData = reader.ReadInt16();
                            span[i] = new Complex(iData, qData);
                        }
                        break;
                    case TypeCode.Int32:
                        length = canReadLength / (2 * sizeof(int));
                        if (length > span.Length)
                        {
                            length = span.Length;
                        }

                        for (int i = 0; i < length; i++)
                        {
                            iData = reader.ReadInt32();
                            qData = reader.ReadInt32();
                            span[i] = new Complex(iData, qData);
                        }
                        break;
                    case TypeCode.Int64:
                        length = canReadLength / (2 * sizeof(long));
                        if (length > span.Length)
                        {
                            length = span.Length;
                        }

                        for (int i = 0; i < length; i++)
                        {
                            iData = reader.ReadInt64();
                            qData = reader.ReadInt64();
                            span[i] = new Complex(iData, qData);
                        }
                        break;
                    case TypeCode.Single:
                        length = canReadLength / (2 * sizeof(float));
                        if (length > span.Length)
                        {
                            length = span.Length;
                        }

                        for (int i = 0; i < length; i++)
                        {
                            iData = reader.ReadSingle();
                            qData = reader.ReadSingle();
                            span[i] = new Complex(iData, qData);
                        }
                        break;
                    case TypeCode.Double:
                        length = canReadLength / (2 * sizeof(double));
                        if (length > span.Length)
                        {
                            length = span.Length;
                        }

                        for (int i = 0; i < length; i++)
                        {
                            iData = reader.ReadDouble();
                            qData = reader.ReadDouble();
                            span[i] = new Complex(iData, qData);
                        }
                        break;
                    case TypeCode.Decimal:
                        length = canReadLength / (2 * sizeof(decimal));
                        if (length > span.Length)
                        {
                            length = span.Length;
                        }

                        for (int i = 0; i < length; i++)
                        {
                            iData = (double)reader.ReadDecimal();
                            qData = (double)reader.ReadDecimal();
                            span[i] = new Complex(iData, qData);
                        }
                        break;
                    default:
                        throw new NotImplementedException($"未实现的文件数据读取类型\"{fileTypeCode.ToString()}\"");
                }
                return (int)length;
            }
        }

        /// <summary>
        /// 从文件读取IQ数据
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="position">读取数据起始位置</param>
        /// <param name="fileTypeCode">文件中存储的数据类型</param>
        /// <param name="span">数据缓存</param>
        /// <returns>读取到的数据长度</returns>
        public static int ReadIQ(string filePath, long position, TypeCode fileTypeCode, IQSpanZ<double> span)
        {
            if (!File.Exists(filePath))
            {
                return 0;
            }

            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                if (filePath.Length <= position)
                {
                    return 0;
                }

                fileStream.Position = position;
                var canReadLength = fileStream.Length - position;

                var reader = new BinaryReader(fileStream);
                long length;
                double* iDataPtr = span.IDataPtr;
                double* qDataPtr = span.QDataPtr;

                switch (fileTypeCode)
                {
                    case TypeCode.SByte:
                        length = canReadLength / (2 * sizeof(sbyte));
                        if (length > span.Length)
                        {
                            length = span.Length;
                        }

                        for (int i = 0; i < length; i++)
                        {
                            *iDataPtr++ = reader.ReadSByte();
                            *qDataPtr++ = reader.ReadSByte();
                        }
                        break;
                    case TypeCode.Int16:
                        length = canReadLength / (2 * sizeof(short));
                        if (length > span.Length)
                        {
                            length = span.Length;
                        }

                        for (int i = 0; i < length; i++)
                        {
                            *iDataPtr++ = reader.ReadInt16();
                            *qDataPtr++ = reader.ReadInt16();
                        }
                        break;
                    case TypeCode.Int32:
                        length = canReadLength / (2 * sizeof(int));
                        if (length > span.Length)
                        {
                            length = span.Length;
                        }

                        for (int i = 0; i < length; i++)
                        {
                            *iDataPtr++ = reader.ReadInt32();
                            *qDataPtr++ = reader.ReadInt32();
                        }
                        break;
                    case TypeCode.Int64:
                        length = canReadLength / (2 * sizeof(long));
                        if (length > span.Length)
                        {
                            length = span.Length;
                        }

                        for (int i = 0; i < length; i++)
                        {
                            *iDataPtr++ = reader.ReadInt64();
                            *qDataPtr++ = reader.ReadInt64();
                        }
                        break;
                    case TypeCode.Single:
                        length = canReadLength / (2 * sizeof(float));
                        if (length > span.Length)
                        {
                            length = span.Length;
                        }

                        for (int i = 0; i < length; i++)
                        {
                            *iDataPtr++ = reader.ReadSingle();
                            *qDataPtr++ = reader.ReadSingle();
                        }
                        break;
                    case TypeCode.Double:
                        length = canReadLength / (2 * sizeof(double));
                        if (length > span.Length)
                        {
                            length = span.Length;
                        }

                        for (int i = 0; i < length; i++)
                        {
                            *iDataPtr++ = reader.ReadDouble();
                            *qDataPtr++ = reader.ReadDouble();
                        }
                        break;
                    case TypeCode.Decimal:
                        length = canReadLength / (2 * sizeof(decimal));
                        if (length > span.Length)
                        {
                            length = span.Length;
                        }

                        for (int i = 0; i < length; i++)
                        {
                            *iDataPtr++ = (double)reader.ReadDecimal();
                            *qDataPtr++ = (double)reader.ReadDecimal();
                        }
                        break;
                    default:
                        throw new NotImplementedException($"未实现的文件数据读取类型\"{fileTypeCode.ToString()}\"");
                }
                return (int)length;
            }
        }






        /// <summary>
        /// 从文件读取数据
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="position">读取数据起始位置</param>
        /// <param name="length">读取数据长度</param>
        /// <returns>读取到的数据</returns>
        public static byte[] Read(string filePath, long position, int length)
        {
            byte[] buffer = new byte[length];
            int readedLen = Read(filePath, position, buffer, 0, length);
            if (readedLen != length)
            {
                return buffer.Take(readedLen).ToArray();
            }
            else
            {
                return buffer;
            }
        }

        /// <summary>
        /// 从文件读取数据
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="position">读取数据起始位置</param>
        /// <param name="buffer">存放读取到的数据缓存</param>
        /// <param name="startIndex">存放数据起始位置</param>
        /// <param name="length">读取数据长度</param>
        /// <returns>读取到的数据长度</returns>
        public static int Read(string filePath, long position, byte[] buffer, int startIndex, int length)
        {
            if (buffer == null ||
                startIndex < 0 ||
                startIndex >= buffer.Length ||
                startIndex + length > buffer.Length)
            {
                return 0;
            }

            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                if (filePath.Length <= position)
                {
                    return 0;
                }

                fileStream.Position = position;
                var canReadLength = fileStream.Length - position;
                if (canReadLength < length)
                {
                    length = (int)canReadLength;
                }

                fileStream.Read(buffer, startIndex, length);
                return length;
            }
        }

        /// <summary>
        /// 从文件读取数据
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="position">读取数据起始位置</param>
        /// <param name="span">存放读取数据的span</param>
        /// <returns>读取到的数据长度</returns>
        public static int Read(string filePath, long position, Span<byte> span)
        {
            if (!File.Exists(filePath) || span.Length <= 0)
            {
                return 0;
            }

            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                if (filePath.Length <= position)
                {
                    return 0;
                }

                fileStream.Position = position;
                var canReadByteLength = fileStream.Length - position;
                int length = span.Length;
                if (canReadByteLength < length)
                {
                    length = (int)canReadByteLength;
                }
                var reader = new BinaryReader(fileStream);

#if NET4_0
                byte* bytePtr = span.DataPtr;
                for (int i = 0; i < length; i++)
                {
                    *bytePtr++ = reader.ReadByte();
                }
#else

                if (length != span.Length)
                {
                    reader.Read(span.Slice(0, length));
                }
                else
                {
                    reader.Read(span);
                }
#endif
                return length;
            }
        }

        /// <summary>
        /// 从文件读取数据
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="position">读取数据起始位置</param>
        /// <param name="span">存放读取数据的span</param>
        /// <returns>读取到的数据长度</returns>
        public static int Read(string filePath, long position, SpanZ<byte> span)
        {
            return Read(filePath, position, span.ToSpan());
        }






        /// <summary>
        /// 从文件读取数据
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="filePath">文件路径</param>
        /// <param name="position">读取数据起始位置</param>
        /// <param name="span">存放读取数据的span</param>
        /// <returns>读取到的数据长度</returns>
        public static int Read<T>(string filePath, long position, SpanZ<T> span) where T : struct
        {
            return Read<T>(filePath, position, span.ToSpan());
        }

        /// <summary>
        /// 从文件读取数据
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="filePath">文件路径</param>
        /// <param name="position">读取数据起始位置</param>
        /// <param name="span">存放读取数据的span</param>
        /// <returns>读取到的数据长度</returns>
        public static int Read<T>(string filePath, long position, Span<T> span) where T : struct
        {
            if (!File.Exists(filePath) || span.Length <= 0)
            {
                return 0;
            }

            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                if (filePath.Length <= position)
                {
                    return 0;
                }

                fileStream.Position = position;
                var canReadByteLength = fileStream.Length - position;
                int canReadLength;
                int length = span.Length;
                var reader = new BinaryReader(fileStream);

                PrimitiveDataTypes primitiveData = TypeEx.GetPrimitiveDataType(default(T));
                switch (primitiveData)
                {
                    case PrimitiveDataTypes.Char:
                        canReadLength = (int)(canReadByteLength / sizeof(char));
                        if (canReadLength < span.Length)
                        {
                            length = canReadLength;
                        }

#if NET4_0
                        char* charPtr = (char*)span.DataPtr;
                        for (int i = 0; i < length; i++)
                        {
                            *charPtr++ = reader.ReadChar();
                        }
        
#else
                        char* charPtr = (char*)Unsafe.AsPointer<T>(ref span[0]);
                        reader.Read(new Span<char>(charPtr, length));
#endif
                        break;
                    case PrimitiveDataTypes.SByte:
                        canReadLength = (int)(canReadByteLength / sizeof(sbyte));
                        if (canReadLength < span.Length)
                        {
                            length = canReadLength;
                        }
#if NET4_0
                        sbyte* sbytePtr = (sbyte*)span.DataPtr;

#else
                        sbyte* sbytePtr = (sbyte*)Unsafe.AsPointer<T>(ref span[0]);
#endif

                        for (int i = 0; i < length; i++)
                        {
                            *sbytePtr++ = reader.ReadSByte();
                        }
                        break;
                    case PrimitiveDataTypes.Byte:
                        canReadLength = (int)(canReadByteLength / sizeof(byte));
                        if (canReadLength < span.Length)
                        {
                            length = canReadLength;
                        }
#if NET4_0
                        byte* bytePtr = (byte*)span.DataPtr;
#else
                        byte* bytePtr = (byte*)Unsafe.AsPointer<T>(ref span[0]);
#endif

                        for (int i = 0; i < length; i++)
                        {
                            *bytePtr++ = reader.ReadByte();
                        }
                        break;
                    case PrimitiveDataTypes.Int16:
                        canReadLength = (int)(canReadByteLength / sizeof(short));
                        if (canReadLength < span.Length)
                        {
                            length = canReadLength;
                        }
#if NET4_0
                        short* shortPtr = (short*)span.DataPtr;
#else
                        short* shortPtr = (short*)Unsafe.AsPointer<T>(ref span[0]);
#endif
                        for (int i = 0; i < length; i++)
                        {
                            *shortPtr++ = reader.ReadInt16();
                        }
                        break;
                    case PrimitiveDataTypes.UInt16:
                        canReadLength = (int)(canReadByteLength / sizeof(ushort));
                        if (canReadLength < span.Length)
                        {
                            length = canReadLength;
                        }
#if NET4_0
                        ushort* ushortPtr = (ushort*)span.DataPtr;
#else
                        ushort* ushortPtr = (ushort*)Unsafe.AsPointer<T>(ref span[0]);
#endif
                        for (int i = 0; i < length; i++)
                        {
                            *ushortPtr++ = reader.ReadUInt16();
                        }
                        break;
                    case PrimitiveDataTypes.Int32:
                        canReadLength = (int)(canReadByteLength / sizeof(int));
                        if (canReadLength < span.Length)
                        {
                            length = canReadLength;
                        }
#if NET4_0
                        int* intPtr = (int*)span.DataPtr;
#else
                        int* intPtr = (int*)Unsafe.AsPointer<T>(ref span[0]);
#endif
                        for (int i = 0; i < length; i++)
                        {
                            *intPtr++ = reader.ReadInt32();
                        }
                        break;
                    case PrimitiveDataTypes.UInt32:
                        canReadLength = (int)(canReadByteLength / sizeof(uint));
                        if (canReadLength < span.Length)
                        {
                            length = canReadLength;
                        }
#if NET4_0
                        uint* uintPtr = (uint*)span.DataPtr;
#else
                        uint* uintPtr = (uint*)Unsafe.AsPointer<T>(ref span[0]);
#endif
                        for (int i = 0; i < length; i++)
                        {
                            *uintPtr++ = reader.ReadUInt32();
                        }
                        break;
                    case PrimitiveDataTypes.Int64:
                        canReadLength = (int)(canReadByteLength / sizeof(long));
                        if (canReadLength < span.Length)
                        {
                            length = canReadLength;
                        }
#if NET4_0
                        long* longPtr = (long*)span.DataPtr;
                        
#else
                        long* longPtr = (long*)Unsafe.AsPointer<T>(ref span[0]);
#endif
                        for (int i = 0; i < length; i++)
                        {
                            *longPtr++ = reader.ReadInt64();
                        }
                        break;
                    case PrimitiveDataTypes.UInt64:
                        canReadLength = (int)(canReadByteLength / sizeof(ulong));
                        if (canReadLength < span.Length)
                        {
                            length = canReadLength;
                        }
#if NET4_0
                        ulong* ulongPtr = (ulong*)span.DataPtr;
#else
                        ulong* ulongPtr = (ulong*)Unsafe.AsPointer<T>(ref span[0]);
#endif
                        for (int i = 0; i < length; i++)
                        {
                            *ulongPtr++ = reader.ReadUInt64();
                        }
                        break;
                    case PrimitiveDataTypes.Float:
                        canReadLength = (int)(canReadByteLength / sizeof(float));
                        if (canReadLength < span.Length)
                        {
                            length = canReadLength;
                        }
#if NET4_0
                        float* floatPtr = (float*)span.DataPtr;
#else
                        float* floatPtr = (float*)Unsafe.AsPointer<T>(ref span[0]);
#endif
                        for (int i = 0; i < length; i++)
                        {
                            *floatPtr++ = reader.ReadSingle();
                        }
                        break;
                    case PrimitiveDataTypes.Double:
                        canReadLength = (int)(canReadByteLength / sizeof(double));
                        if (canReadLength < span.Length)
                        {
                            length = canReadLength;
                        }
#if NET4_0
                        double* doublePtr = (double*)span.DataPtr;
#else
                        double* doublePtr = (double*)Unsafe.AsPointer<T>(ref span[0]);
#endif
                        for (int i = 0; i < length; i++)
                        {
                            *doublePtr++ = reader.ReadDouble();
                        }
                        break;
                    case PrimitiveDataTypes.Decimal:
                        canReadLength = (int)(canReadByteLength / sizeof(decimal));
                        if (canReadLength < span.Length)
                        {
                            length = canReadLength;
                        }
#if NET4_0
                        decimal* decimalPtr = (decimal*)span.DataPtr;
#else
                        decimal* decimalPtr = (decimal*)Unsafe.AsPointer<T>(ref span[0]);
#endif
                        for (int i = 0; i < length; i++)
                        {
                            *decimalPtr++ = reader.ReadDecimal();
                        }
                        break;
                    default:
                        throw new NotImplementedException($"未实现的数据类型读取\"{primitiveData.ToString()}\"");
                }

                return length;
            }
        }


        #endregion

    }
}
